package cylog4go

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
)

const DEBUG_LEVEL string = "DEBUG"
const INFO_LEVEL string = "INFO"
const WARN_LEVEL string = "WARN"
const ERROR_LEVEL string = "ERROR"
const FATAL_LEVEL string = "FATAL"

type outStruct struct {
	Label   string   `json:"label"`
	OutType string   `json:"outType"`
	Params  []string `json:"params"`
}

type labelStruct struct {
	Level   string `json:"level"`
	Prefix  string `json:"prefix"`
	Flag    string `json:"flag"`
	OutType string `json:"outType"`
}

type logConfig struct {
	OutParams []outStruct   `json:"outParams"`
	LogLabel  []labelStruct `json:"logLabel"`
}

var logMap map[string]*log.Logger

func init() {
	logMap = make(map[string]*log.Logger)

	//初始化5个默认日志输出对象
	logMap[DEBUG_LEVEL] = log.New(os.Stdout, getPrefix(DEBUG_LEVEL, ""), log.LstdFlags|log.Lshortfile)
	logMap[INFO_LEVEL] = log.New(os.Stdout, getPrefix(INFO_LEVEL, ""), log.LstdFlags|log.Lshortfile)
	logMap[WARN_LEVEL] = log.New(os.Stdout, getPrefix(WARN_LEVEL, ""), log.LstdFlags|log.Lshortfile)
	logMap[ERROR_LEVEL] = log.New(os.Stdout, getPrefix(ERROR_LEVEL, ""), log.LstdFlags|log.Lshortfile)
	logMap[FATAL_LEVEL] = log.New(os.Stdout, getPrefix(FATAL_LEVEL, ""), log.LstdFlags|log.Lshortfile)
}

func InitJSON(logconfig string) (err error) {

	p := &logConfig{}
	err = json.Unmarshal([]byte(logconfig), p)
	if err != nil {
		err = fmt.Errorf("配置解析失败")

		return
	}

	writerMap, err := generateWriterMap(p.OutParams)
	if err != nil {
		return
	}

	for _, v := range p.LogLabel {

		out, err := getMultiWriter(writerMap, v.OutType)
		if err != nil {
			return err
		}

		logFlag := getLogFlag(v.Flag)

		logMap[strings.ToUpper(v.Level)] = log.New(out, getPrefix(v.Level, v.Prefix), logFlag)
	}

	return
}

func InitSimple(filePath string, isConsole bool) (err error) {
	file := filepath.FromSlash(filePath)
	lfile, err := os.OpenFile(file, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0777)
	if err != nil {
		err = fmt.Errorf("open log file err")

		return err
	}

	var out io.Writer
	if isConsole {
		writers := []io.Writer{
			lfile,
			os.Stdout,
		}
		out = io.MultiWriter(writers...)
	} else {
		out = lfile
	}

	logMap[DEBUG_LEVEL] = log.New(out, DEBUG_LEVEL, log.LstdFlags|log.Lshortfile)
	logMap[INFO_LEVEL] = log.New(out, INFO_LEVEL, log.LstdFlags|log.Lshortfile)
	logMap[WARN_LEVEL] = log.New(out, WARN_LEVEL, log.LstdFlags|log.Lshortfile)
	logMap[ERROR_LEVEL] = log.New(out, ERROR_LEVEL, log.LstdFlags|log.Lshortfile)
	logMap[FATAL_LEVEL] = log.New(out, FATAL_LEVEL, log.LstdFlags|log.Lshortfile)

	return
}

func Debug(format string, v ...interface{}) {
	logger := logMap[DEBUG_LEVEL]

	if logger != nil {
		logger.Printf(format, v...)
	}
}

func Debugln(log string) {
	logger := logMap[DEBUG_LEVEL]

	if logger != nil {
		logger.Println(log)
	}
}

func Info(format string, v ...interface{}) {
	logger := logMap[INFO_LEVEL]

	if logger != nil {
		logger.Printf(format, v...)
	}
}

func Infoln(log string) {
	logger := logMap[INFO_LEVEL]

	if logger != nil {
		logger.Println(log)
	}
}

func Warn(format string, v ...interface{}) {
	logger := logMap[WARN_LEVEL]

	if logger != nil {
		logger.Printf(format, v...)
	}
}

func Warnln(log string) {
	logger := logMap[WARN_LEVEL]

	if logger != nil {
		logger.Println(log)
	}
}

func Error(format string, v ...interface{}) {
	logger := logMap[ERROR_LEVEL]

	if logger != nil {
		logger.Printf(format, v...)
	}
}

func Errorln(log string) {
	logger := logMap[ERROR_LEVEL]

	if logger != nil {
		logger.Println(log)
	}
}

func Fatal(format string, v ...interface{}) {
	logger := logMap[FATAL_LEVEL]

	if logger != nil {
		logger.Printf(format, v...)
	}
}

func Fatalln(log string) {
	logger := logMap[FATAL_LEVEL]

	if logger != nil {
		logger.Println(log)
	}
}

func Other(level, format string, v ...interface{}) {

	levelList := strings.Split(level, "|")

	for _, ll := range levelList {
		logger := logMap[strings.ToUpper(ll)]

		if logger != nil {
			logger.Printf(format, v...)
		}
	}
}

func Otherln(level, log string) {

	levelList := strings.Split(level, "|")

	for _, ll := range levelList {
		logger := logMap[strings.ToUpper(ll)]

		if logger != nil {
			logger.Println(log)
		}
	}
}

func generateWriterMap(outParams []outStruct) (writerMap map[string]io.Writer, err error) {

	writerMap = make(map[string]io.Writer)
	for _, v := range outParams {
		if v.OutType == "File" {
			file := filepath.FromSlash(v.Params[0])
			lfile, err := os.OpenFile(file, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0777)
			if err != nil {
				err = fmt.Errorf("open log file err")

				return nil, err
			}
			writerMap[v.Label] = lfile
		} else {
			err = fmt.Errorf("目前只支持文件日志")
		}
	}

	return
}

func getMultiWriter(writerMap map[string]io.Writer, outType string) (out io.Writer, err error) {

	outTypeList := strings.Split(outType, "|")
	var outList [5]io.Writer // 最大同时支持5个不同的输出
	dwI := 0
	for _, vo := range outTypeList {
		if vo == "Console" {
			outList[dwI] = os.Stdout
		} else {
			oo := writerMap[vo]
			if oo == nil {
				err = fmt.Errorf("无法识别的输出句柄 %s", vo)

				return nil, err
			}
			outList[dwI] = oo
		}
		dwI++
	}

	out = io.MultiWriter(outList[:dwI]...)

	return
}

func getPrefix(level string, prefix string) string {

	if len(prefix) <= 0 {
		var defaultPrefix string
		if strings.Compare(strings.ToUpper(level), DEBUG_LEVEL) == 0 {
			defaultPrefix = DEBUG_LEVEL
		} else if strings.Compare(strings.ToUpper(level), INFO_LEVEL) == 0 {
			defaultPrefix = INFO_LEVEL
		} else if strings.Compare(strings.ToUpper(level), WARN_LEVEL) == 0 {
			defaultPrefix = WARN_LEVEL
		} else if strings.Compare(strings.ToUpper(level), ERROR_LEVEL) == 0 {
			defaultPrefix = ERROR_LEVEL
		} else if strings.Compare(strings.ToUpper(level), FATAL_LEVEL) == 0 {
			defaultPrefix = FATAL_LEVEL
		}

		if len(defaultPrefix) > 0 {
			prefix = "[" + defaultPrefix + "] "
		}
	}

	return prefix

}

func getLogFlag(flagStr string) (logFlag int) {
	flags := strings.Split(flagStr, "|")
	for _, vf := range flags {
		if vf == "Ldate" {
			logFlag &= log.Ldate
		} else if vf == "Ltime" {
			logFlag &= log.Ltime
		} else if vf == "Lmicroseconds" {
			logFlag &= log.Lmicroseconds
		} else if vf == "Llongfile" {
			logFlag &= log.Llongfile
		} else if vf == "Lshortfile" {
			logFlag &= log.Lshortfile
		} else if vf == "LUTC" {
			logFlag &= log.LUTC
		}
	}

	if logFlag == 0 {
		logFlag = log.LstdFlags | log.Lshortfile // 默认LstdFlags
	}

	return
}
